<template>
  <Black :showBlack="showBack">
    <div id="imgCanvas">
      <div class="bottom">
        <TButton @click="completeFile">确认</TButton>
        <TButton @click="reset">重置</TButton>
        <TButton @click="closeModal" type="info">取消</TButton>
        <TButton @click="canvasToBase64">生成图片</TButton>
      </div>
      <div class="canvasBox" :style="{ scale: canvasScale }">
        <canvas
          class="beforeCanvas"
          ref="canvas"
          :width="canvasWidth"
          :height="canvasHeight"
          @mousedown="getStart"
          @mouseup="getEnd"
        ></canvas>
        <canvas
          class="showCanvas"
          style="circle && {border-radius: 50%;}"
          ref="canvasShow"
          :width="canvasWidth / 2"
          :height="canvasWidth / 2"
          @mousedown="getStart"
          @mouseup="getEnd"
        ></canvas>
      </div>
    </div>
  </Black>
</template>

<script lang="ts" setup>
import Black from './black.vue'
import { onMounted, ref, defineProps, defineExpose, watch, nextTick, defineEmits } from 'vue'
import TButton from '@/components/button.vue'
const props = defineProps({
  imgUrl: {
    type: String,
    default: ''
  },
  circle: {
    type: Boolean,
    default: true
  }
})
const showBack = ref<boolean>(false)
const ctx = ref<CanvasRenderingContext2D | null | undefined>(null)
const ctxShow = ref<CanvasRenderingContext2D | null | undefined>(null)
const canvas = ref<HTMLCanvasElement | null>(null)
const canvasShow = ref<HTMLCanvasElement | null>(null)
const imgRef = ref<HTMLImageElement | null>(null)
const StartX = ref<number>(0)
const StartY = ref<number>(0)
const EndW = ref<number>(0)
const EndH = ref<number>(0)
const rx = ref<number>(0)
const ry = ref<number>(0)
const temrx = ref<number>(0)
const temry = ref<number>(0)
const flagArc = ref<boolean>(false)
const canvasFile = ref<File | null>(null)
const canvasHeight = ref<number>(0)
const canvasWidth = ref<number>(0)
const canvasScale = ref<number>(0)

const emit = defineEmits(['canvasFile', 'close'])
onMounted(() => {
  const div = document.createElement('div')
  div.style.overflow = 'hidden'
  div.style.width = '100px'
  div.style.height = '100px'
  div.style.position = 'absolute'
  const img = document.createElement('img')
  img.crossOrigin = 'anonymous'
  imgRef.value = img
  img.style.position = 'absolute'
  img.style.zIndex = '-1'
  div.appendChild(img)
  document.body.appendChild(div)
})
watch(
  () => props.imgUrl,
  (val) => {
    if (val) {
      nextTick(() => {
        reset()
        ctx.value = canvas.value?.getContext('2d')
        ctxShow.value = canvasShow.value?.getContext('2d')
        imgRef.value!.src = val
        imgRef.value!.onload = () => {
          const { width, height } = imgRef.value?.getBoundingClientRect()!
          canvasScale.value = 400 / width
          canvasHeight.value = height
          canvasWidth.value = width
          nextTick(() => {
            ctx.value?.drawImage(imgRef.value!, 0, 0, canvasWidth.value, canvasHeight.value)
            ctxShow.value?.drawImage(imgRef.value!, 0, 0, canvasWidth.value / 2, canvasWidth.value / 2)
          })
        }
      })
    }
  }
)
const getStart = (e: MouseEvent) => {
  MouseDown(e)
}
const getEnd = () => {
  if (flagArc.value) {
    rx.value = temrx.value
    ry.value = temry.value
  }

  flagArc.value = true
  confirm()
  canvas.value?.removeEventListener('mousemove', getMove)
}
const getMove = (e: MouseEvent) => {
  confirm()
  flagArc.value ? moveArc(e) : MouseMove(e)
}
const MouseDown = (e: MouseEvent) => {
  if (!flagArc.value) {
    resetImg()
  }
  StartX.value = e.offsetX
  StartY.value = e.offsetY
  canvas.value?.addEventListener('mousemove', getMove)
}

const closeModal = () => {
  emit('close', canvasFile.value)
  showBack.value = false
}
const MouseMove = (e: MouseEvent) => {
  resetImg()
  const w = EndW.value
  const h = EndH.value
  const x = rx.value
  const y = ry.value
  EndW.value = e.offsetX - StartX.value
  EndH.value = e.offsetY - StartY.value
  rx.value = (StartX.value + e.offsetX) / 2
  ry.value = StartY.value
  ctx.value?.beginPath()
  ctx.value!.lineWidth = (0.5 * canvasWidth.value) / 400

  if (
    ry.value < Math.abs(EndW.value) / 2 ||
    ry.value + Math.abs(EndW.value) / 2 > canvasHeight.value ||
    rx.value + Math.abs(EndW.value) / 2 > canvasWidth.value
  ) {
    // 高度超出
    EndW.value = w
    EndH.value = h
    rx.value = x
    ry.value = y
    ctx.value?.arc(x, y, Math.abs(w) / 2, 0, 2 * Math.PI)
    temrx.value = x
    temry.value = y
  } else {
    temrx.value = rx.value
    temry.value = ry.value
    ctx.value?.arc(rx.value, ry.value, Math.abs(EndW.value) / 2, 0, 2 * Math.PI)
  }
  ctx.value?.stroke()
}
const moveArc = (e: MouseEvent) => {
  resetImg()
  ctx.value?.beginPath()
  const resX = rx.value + e.offsetX - StartX.value
  const resY = ry.value + e.offsetY - StartY.value
  let nextX = resX
  let nextY = resY
  if (resY < Math.abs(EndW.value) / 2 || resY + Math.abs(EndW.value) / 2 > canvasHeight.value) {
    // top 超出
    nextY = temry.value
  }
  if (resX < Math.abs(EndW.value) / 2 || resX + Math.abs(EndW.value) / 2 > canvasWidth.value) {
    // ctx.value?.arc(temrx.value, resY, Math.abs(EndW.value) / 2, 0, 2 * Math.PI);
    nextX = temrx.value
  }
  ctx.value?.arc(nextX, nextY, Math.abs(EndW.value) / 2, 0, 2 * Math.PI)

  temrx.value = nextX
  temry.value = nextY

  ctx.value?.stroke()
}
const loadImg = () => {
  ctx.value?.drawImage(imgRef.value!, 0, 0, canvasWidth.value, canvasHeight.value)
}
const canvasToBase64 = () => {
  const file = addFile()
  var aDom = document.createElement('a') // 创建一个 a 标签
  aDom.download = file.name // 设置文件名
  let href = URL.createObjectURL(file) // 将file对象转成 UTF-16 字符串
  aDom.href = href // 放入href
  document.body.appendChild(aDom) // 将a标签插入 body
  aDom.click() // 触发 a 标签的点击
  document.body.removeChild(aDom) // 移除刚才插入的 a 标签
  URL.revokeObjectURL(href) // 释放刚才生成的 UTF-16 字符串
}
const addFile = (quality: number = 1): File => {
  // 默认是png
  const url = canvasShow.value!.toDataURL('image/jpeg', quality) // 第二个参数为转化的图片质量
  const arr = url.split(',')
  const mime = arr[0].match(/:(.*?);/)![1] // 此处得到的为文件类型
  const bstr = atob(arr[1]) // 此处将base64解码
  let n = bstr.length
  const u8arr = new Uint8Array(n)
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n)
  }
  // 通过以下方式将以上变量生成文件对象，三个参数分别为文件内容、文件名、文件类型
  const file = new File([u8arr], 'picture.jpg', { type: mime })
  canvasFile.value = file
  return file
}
const resetImg = () => {
  ctx.value?.clearRect(0, 0, canvasWidth.value, canvasHeight.value)
  loadImg()
}
const reset = () => {
  resetImg()
  StartX.value = 0
  StartY.value = 0
  EndH.value = 0
  EndW.value = 0
  rx.value = 0
  ry.value = 0
  temrx.value = 0
  temry.value = 0
  flagArc.value = false
  ctxShow.value?.drawImage(imgRef.value!, 0, 0, canvasWidth.value / 2, canvasWidth.value / 2)
}
const confirm = () => {
  const w = Math.abs(EndW.value)
  ctxShow.value?.drawImage(
    imgRef.value!,
    temrx.value - w / 2,
    temry.value - w / 2,
    w,
    w,
    0,
    0,
    canvasWidth.value / 2,
    canvasWidth.value / 2
  )
}
const completeFile = () => {
  addFile(0.4)
  emit('canvasFile', canvasFile.value)
  showBack.value = false
}

defineExpose({ showBack, canvasToBase64 })
</script>

<style scoped lang="scss">
#imgCanvas {
  z-index: 9;
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  margin: auto;
  // width: 800px;
  height: 500px;
  .canvasBox {
    display: flex;
    justify-content: center;
    transform-origin: top;
    margin-top: 30px;
    align-items: center;
    .beforeCanvas {
      background-color: #fff;
      height: fit-content;
      margin: 24px;
    }
    .showCanvas {
      height: fit-content;
      background-color: #fff;
      // margin-left: 16px;
      margin: 24px;
    }
  }
}
img {
  // width: 400px;
  position: relative;
  z-index: -1;
}
.bottom {
  display: flex;
  justify-content: center;
  padding: 0 150px;
  margin-top: 10px;
  & > div {
    margin: 0 16px;
  }
}
</style>
